package serial;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Scanner;
import java.util.TreeSet;

public class SerialInterface implements SerialReaderListener {
	private final static boolean debug = false;
	
	private boolean fullLibrary;
	private String libraryFilename;
	private String libraryCheckCmd;
	private TreeSet<String> possibleNames;
	private ArrayList<SerialReaderListener> readerListeners;
	private CmdSender lackey;
	private String bestPort;
	
	public SerialInterface(SerialReaderListener... listeners) {
		enableFullLibrary();
		possibleNames = new TreeSet<String>();
		readerListeners = new ArrayList<SerialReaderListener>();
		for (SerialReaderListener srl: listeners) {
			readerListeners.add(srl);
		}
		lackey = null;
		bestPort = null;
	}
	
	public void addSerialReaderListener(SerialReaderListener srl) {
		readerListeners.add(srl);
	}
	
	public boolean hasEnabledLibrary() {
		return libraryFilename != null;
	}
	
	public boolean hasEnabledFullLibrary() {
		return fullLibrary;
	}
	
	public void disableLibrary() {
		fullLibrary = false;
		libraryFilename = libraryCheckCmd = null;
	}
	
	public void enableFullLibrary() {
		fullLibrary = true;
		libraryFilename = "gjflib";
		libraryCheckCmd = LibraryChecker.makeCheckerWith("write");
	}
	
	public void enableSonarLibrary() {
		fullLibrary = false;
		libraryFilename = "sonarlib";
		libraryCheckCmd = LibraryChecker.makeCheckerWith("sonarReading");
	}
	
	public boolean inProgress() {
		return lackey != null && !lackey.isCompleted();
	}
	
	public boolean hasLivePort() {
		return getBestPort() != null;
	}
	
	public String getBestPort() {
		return bestPort;
	}
	
	public void findBestPort() {
		if (bestPort == null || !PortChecker.isPortActive(bestPort)) {
			for (String port: possibleNames) {
				System.out.println("Checking " + port);
				if (PortChecker.isPortActive(port)) {
					bestPort = port;
					return;
				}
			}
		}
	}

	public void downloadLibrary() throws SerialException, FileNotFoundException {
		if (inProgress()) {
			lackey.halt();
			throw new PortInUseException();
		} else {
			findBestPort();
			if (hasLivePort()) {
				//LibraryChecker.sendLibraryToFlash(bestPort);
				LibraryChecker.sendLibrary(bestPort, libraryFilename);
			} else {
				throw new NoSuchPortException();
			}
		}
	}
	
	public void send(String msg, boolean disconnectAfter) throws SerialException {
		send(msg, disconnectAfter, false);
	}
	
	public void send(String msg, boolean disconnectAfter, boolean ignoreLibrary) throws SerialException {
		try {
			if (inProgress()) {
				lackey.halt();
				throw new PortInUseException();
			} else {
				findBestPort();
				if (hasLivePort()) {
					if (!ignoreLibrary && hasEnabledLibrary() && !LibraryChecker.isLibraryInPlace(bestPort, libraryCheckCmd)) {
						LibraryChecker.sendLibrary(bestPort, libraryFilename);
					}
					lackey = new CmdSender(bestPort, msg, disconnectAfter, readerListeners);
					lackey.start();
				} else {
					throw new NoSuchPortException();
				}
			}
		} catch (IOException ioe) {
			throw new SerialException(ioe);
		}
	}
	
	public void suggestBestPort(String port) {
		bestPort = port;
		addPossibleName(port);
	}
	
	public void addPossibleName(String value) {
		possibleNames.add(value);
	}
	
	public Collection<String> getPossibleNames() {
		return Collections.unmodifiableCollection(possibleNames);
	}
	
	public void loadPossibleNamesFrom(String filename) throws FileNotFoundException {
		Scanner s = new Scanner(new File(filename));
		while (s.hasNextLine()) {
			addPossibleName(s.nextLine());
		}
		s.close();
	}

	@Override
	public void halted() {
		lackey = null;
	}

	@Override
	public void stringReceived(String input) {}
}
